package test.gpstracker;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;


import test.gpstracker.database.DBUtil;
import test.gpstracker.model.HistoryDAO;
import test.gpstracker.model.HistoryItem;
import test.gpstracker.model.LocationDAO;
import test.gpstracker.model.LocationItem;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
import android.support.v4.app.NotificationCompat;
import android.util.Log;

public class LocationBroadcast extends BroadcastReceiver implements LocationListener
{    
	private Context context;
	private String API_KEY = "AIzaSyBHdhujRQO0J34pUEgGOtnFQjqASQ0hcwQ";
	protected LocationManager locationManager;
	private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 100; // 10 meters
	// The minimum time between updates in milliseconds
	private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 1; // 1 minute
	Location location;// search result from API
	boolean isGPSEnabled = false;
	boolean isNetworkEnabled = false;
	boolean canGetLocation = false;
	
	@Override
	public void onReceive(Context context, Intent intent) 
	{   
		PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
		PowerManager.WakeLock wakerlocLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "");
		wakerlocLock.acquire();
		this.context = context;
		try {
			DBUtil.getInstance(context);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		GetPlaces aGetPlaces= new GetPlaces();//get current location and call Google API
		aGetPlaces.execute();
		searchUnknowLocation();
		wakerlocLock.release();
	}

	public void SetAlarm(Context context)
	{
		AlarmManager am=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
		Intent i = new Intent(context, LocationBroadcast.class);
		PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
		am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 1000 * 50, pi); // Millisec * Second * Minute
		this.context = context;
		
	}

	public void CancelAlarm(Context context)
	{
		Intent intent = new Intent(context, LocationBroadcast.class);
		PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, 0);
		AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
		alarmManager.cancel(sender);
	}

	//search place near current location by using google API
	public LocationItem findPlaces(double latitude, double longitude) 
	{
		String urlString = makeUrl(latitude, longitude);
		try 
		{
			String json = getJSON(urlString);
			System.out.println(json);
			JSONObject object = new JSONObject(json);
			JSONArray array = object.getJSONArray("results");
			LocationItem tmplocation = null;
			try {
				tmplocation = LocationItem.jsonToPontoReferencia((JSONObject) array.get(0));
			} catch (Exception e) {
			}
			return tmplocation;

		} catch (JSONException ex) {
			Logger.getLogger(LocationBroadcast.class.getName()).log(Level.SEVERE,
					null, ex);
		}
		return null;
	}

	//get name of unknow location. No internet access scenario
	private void searchUnknowLocation()
	{
		LocationDAO dao = new LocationDAO(DBUtil.getReadableDatabase());
		List<LocationItem> unknowList = dao.getItemsAllLocation();
		if (unknowList.size()>0) {
			for (int i = 0; i < unknowList.size(); i++) {
				location.setLatitude(unknowList.get(i).getLatitude());
				location.setLongitude(unknowList.get(i).getLongitude());
				GetPlaces getPlaces = new GetPlaces();
				getPlaces.execute();
			}
		}
	}
	//search place near current location when internet is disable 
	public LocationItem findPlaceInDB(Location currentLocation)
	{
		LocationItem result = null;
		LocationDAO dao = new LocationDAO(DBUtil.getWritableDatabase());
		List<LocationItem> dbList = dao.selectUnnamedLocation();
		float distance=10;
		for (int i = 0; i < dbList.size(); i++) {
			Location tmp = dbList.get(i).getLocation();
			float dis = location.distanceTo(tmp);
			if (dis<distance) {
				distance = dis;
				result = dbList.get(i);
			}
		}
		return result;
	}

	private String makeUrl(double latitude, double longitude) {
		StringBuilder urlString = new StringBuilder("https://maps.googleapis.com/maps/api/place/search/json?");
		urlString.append("&location=");
		urlString.append(Double.toString(latitude));
		urlString.append(",");
		urlString.append(Double.toString(longitude));
		urlString.append("&radius=10");
		urlString.append("&sensor=false&key=" + API_KEY);
		return urlString.toString();
	}

	protected String getJSON(String url) {
		return getUrlContents(url);
	}

	//get reponse from API
	private String getUrlContents(String theUrl) {
		StringBuilder content = new StringBuilder();
		try {
			URL url = new URL(theUrl);
			URLConnection urlConnection = url.openConnection();
			BufferedReader bufferedReader = new BufferedReader(
					new InputStreamReader(urlConnection.getInputStream()), 8);
			String line;
			while ((line = bufferedReader.readLine()) != null) {
				content.append(line + "\n");
			}
			bufferedReader.close();
		}catch (Exception e) {
			e.printStackTrace();
		}
		return content.toString();
	}
	
	//get current location
	public Location getLocation() {
		try {
			locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
			isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
			isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
			if (!isGPSEnabled && !isNetworkEnabled) {
			} else 
			{
				this.canGetLocation = true;
				//  get location using GPS Services
				if (isGPSEnabled) {
					if (location == null) {
						locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,MIN_TIME_BW_UPDATES,MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
						Log.d("GPS", "GPS Enabled");
						if (locationManager != null) {
							location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);		
						}
					}
				}

				if (location==null) {
					if (isNetworkEnabled) {
						locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,MIN_TIME_BW_UPDATES,MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
						Log.d("Network", "Network Enabled");
						if (locationManager != null) {
							location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);	
						}
					}
				}

			}

		} catch (Exception e) {
			e.printStackTrace();
		}
		return location;
	}

	//show alert when GPS is not enable
	public void showSettingsAlert(){
		AlertDialog.Builder alertDialog = new AlertDialog.Builder(context);
		alertDialog.setTitle("GPS is settings");
		alertDialog.setMessage("GPS is not enabled. Do you want to go to settings menu?");
		alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog,int which) {
				Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
				context.startActivity(intent);
			}
		});
		alertDialog.setNeutralButton("Cancel", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int which) {
				dialog.cancel();
			}
		});
		alertDialog.show();
	}
	
	private class GetPlaces extends AsyncTask<Void,Void , LocationItem> {
		NotificationCompat.Builder mBuilder =
				new NotificationCompat.Builder(context).setContentTitle("GPS Tracker is working").setContentText("GPS Traker records your location");
		NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
		
		public GetPlaces() {
			mNotificationManager.notify(1, mBuilder.build());
			CharSequence tickerText = "Hello";
			long when = System.currentTimeMillis();
			Notification notification = new Notification(R.drawable.icon_place_area, tickerText, when);
			Intent notificationIntent = new Intent(context, StartActivity.class);
			PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
			notification.setLatestEventInfo(context, "GPS", "GPS2", contentIntent);
			NotificationManager mNotificationManager = (NotificationManager)context. getSystemService(Context.NOTIFICATION_SERVICE);
			mNotificationManager.notify(1, notification);
		}

		@Override
		protected void onPostExecute(LocationItem result) {
			super.onPostExecute(result);
			HistoryItem history = new HistoryItem();
			history.setCheckinTime(new Date());
			if (result == null) {
				Log.v("LocationBroadcast","cannot get location name");
				result = new LocationItem();
				result.setLongitude(location.getLongitude());
				result.setLatitude(location.getLatitude());
				result.setUpdated(false);
			}
			else {
				Log.v("LocationBroadcast","location name" + result.getLocationName());
			}
			history.setLocation(result);
			insertHistory(history);
		}

		@Override
		protected void onPreExecute() {
			super.onPreExecute();

			location=getLocation();
		}

		@Override
		protected LocationItem doInBackground(Void... arg0) {
			LocationItem findPlaces = findPlaces(location.getLatitude(),location.getLongitude()); // 77.218276
			if (findPlaces == null) {
				findPlaces = findPlaceInDB(location);
			}
			return findPlaces;
		}
		
		//insert location (result from API) to database
		private boolean insertHistory(HistoryItem history)
		{
			final SQLiteDatabase db = DBUtil.getReadableDatabase();
			LocationDAO dao = new LocationDAO(DBUtil.getWritableDatabase());
			HistoryDAO dao2 = new HistoryDAO(DBUtil.getWritableDatabase());
			try
			{
				int locationID = 0;
				db.beginTransaction();	
				LocationItem tmp = dao.selectByCoordinates(history.getLocation().getLongitude(), history.getLocation().getLatitude());
				if (tmp==null) 
				{
					//this location haven't stored in database yet
					dao.insert(history.getLocation());
					locationID = dao.selectMaxID().getID();
				}
				else {
					//this location have stored in database already
					locationID = tmp.getID();
				}
				dao2.insert(locationID, new Date());//insert new history to database
				db.setTransactionSuccessful();
				return true;
			}
			catch(Exception ex)
			{
				Log.e("insert data error", ex.getMessage());
			}
			finally
			{
				db.endTransaction();
			}
			return false;
			
		}
	}

	@Override
	public void onLocationChanged(Location location) {
		// TODO Auto-generated method stub
		Log.d("TAG","ON Change Location....return in isGpsUpdate");
		if (locationManager != null) {
			location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);	
		}
	}


	@Override
	public void onProviderDisabled(String provider) {
		// TODO Auto-generated method stub

	}


	@Override
	public void onProviderEnabled(String provider) {
		// TODO Auto-generated method stub

	}


	@Override
	public void onStatusChanged(String provider, int status, Bundle extras) {
		// TODO Auto-generated method stub

	}

}